import { convertObject2FormData } from '../utils/utils';
import { httpRequest } from '../utils/httpRequest';
import { uploadParamsType } from '../type';

/**
 * 直传服务器
 */
export function upload(params: uploadParamsType): Promise<any> {
  return new Promise(async (resolve, reject) => {
    const file = params.file;
    // 校验参数
    if (!file) return reject('未提供上传文件');
    const {
      maxSize = -1,
      partSize = -1,
      mergeUrl = '',
      queueLength = 0,
      uploadUrl = '',
    } = params;
    if (!uploadUrl) return reject('上传地址不能为空');
    // 是否采用分片
    const isPart = maxSize > 0 && file.size > maxSize;

    // 普通上传
    if (!isPart) {
      // 函数引用传递
      let uploadAbort: any = {
        instance: null,
      };
      // 注册中断
      let Abort: any = {};
      Abort.cancel = function () {
        uploadAbort.instance?.abort();
      };
      params.onCancel && params.onCancel(Abort);
      // 组装上传参数
      let formData = new FormData();
      formData.append('file', file);
      if (params.fileName) formData.append('fileName', params.fileName);

      httpRequest({
        url: uploadUrl,
        method: 'post',
        data: formData,
        onProgress: params.onUploadProgress,
        xhrInstance: uploadAbort,
      })
        .then((res) => {
          resolve(res);
        })
        .catch((error) => {
          reject(error);
        });
    }

    // 切片上传
    if (isPart) {
      if (partSize <= 0) return reject('分片大小不能小于0');
      if (!mergeUrl) return reject('分片合并地址不能为空');
      // 切片总数
      const chunks: number = Math.ceil(file.size / partSize);

      let uploadList: any[] = [];
      let mergeAbort: any = {
        instance: null,
      };
      // 注册中断
      let Abort: any = {};
      Abort.cancel = function () {
        // 中断切片上传
        uploadList.forEach((item) => {
          item.instance?.abort();
        });
        // 中断合并
        mergeAbort.instance?.abort();
      };
      params.onCancel && params.onCancel(Abort);
      // 按照生成随机uploadId
      let uploadId =
        new Date().getTime().toString() + (Math.random() * 100000).toFixed(0);

      for (let i = 1; i <= chunks; i++) {
        const start = (i - 1) * partSize;
        const end = Math.min(start + partSize, file.size) - 1;

        uploadList.push({
          chunk: i,
          file: file.slice(start, end + 1),
          uploadId,
          // 中断
          instance: null,
        });
      }

      // 队列上传
      for (let i: number = 0; i < queueLength; i++) {
        uploadList[i].uploading = true;
        loop(uploadList[i]);
      }

      // 上传切片
      function upload(item: any): Promise<any> {
        return new Promise((resolve, reject) => {
          let data = new FormData();
          data.append('file', item.file);
          data.append('uploadId', item.uploadId);
          data.append('chunk', item.chunk);
          data.append('chunks', chunks.toString());
          httpRequest({
            url: params.uploadUrl,
            method: 'post',
            data,
            xhrInstance: item,
          })
            .then((data: any) => {
              resolve(data);
            })
            .catch((err: any) => {
              reject(err);
            });
        });
      }

      // 加入队列
      function loop(item: any) {
        upload(item)
          .then(() => {
            item.uploaded = true;
            let uploadedLen: number = uploadList.filter(
              (var0: any) => var0.uploaded
            ).length;
            // 按照切片完成数量来返回进度
            params.onUploadProgress &&
              params.onUploadProgress({
                loaded: Math.floor((uploadedLen / chunks) * file.size),
                total: file.size,
              });
            // 找出未开始上传的文件
            let index = uploadList.findIndex(
              (item) => !item.uploading && !item.uploaded
            );
            if (index > -1) {
              uploadList[index].uploading = true;
              loop(uploadList[index]);
            }
            // 全部上传完毕，开始合并
            if (uploadList.every((var0: any) => var0.uploaded)) {
              httpRequest({
                url: `${mergeUrl}?uploadId=${uploadId}&fileName=${params.fileName}`,
                method: 'get',
                xhrInstance: mergeAbort,
              })
                .then((res: any) => {
                  resolve(res);
                })
                .catch((err: any) => {
                  reject(err);
                });
            }
          })
          .catch((err) => {
            reject(err);
          });
      }
    }
  });
}
